'''
Created on Jan 25, 2011

@author: dexen
'''

import os

import rpyc

from dexen.libs import ranking
from dexen.system.common.meta import ScoreMeta
from dexen.system.common import service, utils
from dexen.system.common.base import Action, Individual
from dexen.system.server.archive_server import PORT as ARCH_SRV_PORT
from dexen.libs.ranking import ParetoRanking, RankingResult
from dexen.system.common.excepts import ConnectionIsNoneException, JobNotExistsException    
from rpyc.utils.factory import DiscoveryError


#===============================================================================
# Server Proxy
#===============================================================================
class ServerProxy(object):
    def __init__(self):
        self.srv_conn = None

    def check_connection(self):
        if not self.srv_conn:
            raise ConnectionIsNoneException("Server is unreachable. Please connect first.")

    def connect(self, reg_ip, reg_name):
        try:
            (ip, port) = rpyc.discover(reg_name, reg_ip)[0]
            self.srv_conn = service.get_svr_conn(ip)
        except DiscoveryError:
            try:
                print "I am in first discovery error"
                (ip, port) = rpyc.discover(reg_name)[0]
                self.srv_conn = service.get_svr_conn(ip)
            except DiscoveryError:
                print "I am in second discovery error"
                self.srv_conn = service.get_svr_conn(reg_ip)
        print "Connected"

    def get_address(self):
        """Get the address (ip,port) of http server running 
        on the server for archived data
        """
        self.check_connection()
        ip = self.srv_conn.root.get_ip()
        port = ARCH_SRV_PORT
        return ip, port

    def start_job(self, job_name, job_def_dir):
        job_def_zip = utils.get_schema_zip(job_def_dir) 
        self.srv_conn.root.start_job(job_def_zip, job_name)
        
    def get_job_states(self):
        job_states = self.srv_conn.root.get_job_states()
        job_states = rpyc.classic.obtain(job_states)
        return job_states
    
    def get_job(self, job_id):
        jobs = self.get_job_states()
        job_ids = [job.job_id for job in jobs]
        if not job_id in job_ids:
            raise JobNotExistsException("Job with id:{0} doesn't exist".format(job_id))
        return JobProxy(job_id, self.srv_conn)


#===============================================================================
# Job Proxy
#===============================================================================
class JobProxy(object):
    def __init__(self, job_id, srv_conn):
        self.job_id = job_id
        self.srv_conn = srv_conn
        self.actions = []
        self.inds = {}
        self.last_act_id = 0
        self.recent_acts = []
        self.p_ranking = ParetoRanking()
    
    def _process(self, actions):
        for action in actions:
            assert isinstance(action, Action)
            inds = action.get_inds()
            for ind in inds:
                assert isinstance(ind, Individual)
                self.inds[ind._get_id()] = ind

    def stop(self):
        self.srv_conn.root.stop_job(self.job_id)
    
    def delete(self):
        self.srv_conn.root.del_job(self.job_id)
       
    def update(self):
        new_acts = self.srv_conn.root.update(self.job_id, self.last_act_id)
        new_acts = rpyc.classic.obtain(new_acts)
        if len(new_acts) > 0:
            action = new_acts[-1]
            assert isinstance(action, Action)
            self.last_act_id = action.get_id()
        self.actions.extend(new_acts)
        self.recent_acts.extend(new_acts)
        self._process(new_acts)
        return len(new_acts)
    
    def get_recent_actions(self):
        acts = self.recent_acts
        self.recent_acts = []
        return acts
        
    def get_all_actions(self):
        return self.actions
    
    def add_score(self, name, type):
        type = type.upper()
        if type == "MIN":
            self.p_ranking.register_score(name, ranking.MINIMIZE)
        elif type == "MAX": 
            self.p_ranking.register_score(name, ranking.MAXIMIZE)

    def del_score(self, name):
        self.p_ranking.deregister_score(name)
    
    def get_score_metas(self):
        return self.p_ranking.get_scores()
    
    def get_action_log(self):
        log = ""
 
        if len(self.actions) > 0:
            action = self.actions[-1]
            assert isinstance(action, Action)
            log += action.get_header() + "\n"
        
        for action in self.actions:
            assert isinstance(action, Action)
            log += action.get_log() + "\n"
        return log
    
    def get_pareto_log(self, max_level):
        log = ""
        inds = self.inds.values()      
        self.p_ranking.set_inds(inds)
        ranked = self.p_ranking.rank(max_level)
        scores = self.p_ranking.get_scores()
        header = Individual._get_header()
        for score_meta in scores:
            assert isinstance(score_meta, ScoreMeta)
            header += ",{0}".format(score_meta.name)
        log += header + ",Rank\n"
                
        for rank_result in ranked:
            assert isinstance(rank_result, RankingResult)
            ind = rank_result.ind
            log += ind._get_log()
            for score_meta in scores:
                log += ",{0}".format(getattr(ind, score_meta.name))
            log += ",{0}\n".format(rank_result.rank)
        
        for unranked_ind in self.p_ranking.get_unranked():
            assert isinstance(unranked_ind, Individual)
            log += unranked_ind._get_log()
            for score_meta in scores:
                log += ",{0}".format(getattr(unranked_ind, score_meta.name))
            log += ",None\n"
        
        return log   
    
    
